Skip to content

fix: register Value subclass auto-methods in class hierarchy before type checking#1018

Merged
jamesc merged 5 commits intomainfrom
worktree-bug/value-class-new
Feb 28, 2026
Merged

fix: register Value subclass auto-methods in class hierarchy before type checking#1018
jamesc merged 5 commits intomainfrom
worktree-bug/value-class-new

Conversation

@jamesc
Copy link
Owner

@jamesc jamesc commented Feb 28, 2026

Summary

  • Bug: Value subclass: classes with auto-generated keyword constructors (e.g., Point x:y:) failed type checking when referenced in method bodies of the same class ("Point class does not understand 'x:y:'")
  • Root cause: Auto-generated slot methods (getters, with*: setters, keyword constructors) were only synthesized during codegen, but the type checker runs before codegen
  • Fix: Synthesize auto-methods in the class hierarchy during semantic analysis so they're visible to the type checker
  • Bonus: Extended the effect-free statement lint to catch discarded MapLiteral, ArrayLiteral, and ListLiteral expressions (with recursive purity checks)

Key Changes

  • class_hierarchy/mod.rs: Added add_value_auto_methods() — mirrors codegen's compute_auto_slot_methods logic
  • validators.rs: is_effect_free now recursively checks map/array/list contents for side effects
  • effect_free_statement.rs: Added tests for discarded map literal lint (pure vs side-effectful)

Follow-up

  • BT-979: REPL effect-free warning for module-level expressions

Test Plan

  • Existing ValueSubclassTest (11 tests) continues passing
  • examples/getting-started/src/point.bt compiles cleanly (previously errored)
  • New unit tests for auto-method synthesis and user-override respect
  • New lint tests for discarded map literal (pure) and map with side-effects (not flagged)
  • Full CI: 2,276 Rust + 237 stdlib + 645 BUnit + 2,208 Erlang tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Auto-generation of getters, setters, and keyword constructors for Value subclasses; now exposed in runtime reflection metadata.
    • Literals (map/array/list) are considered effect-free in relevant linting contexts.
  • Improvements

    • Assigning to another object's field now emits a warning with a clear, actionable hint suggesting a functional update pattern.
  • Tests

    • Expanded tests covering map-literal linting, value-subclass auto-generation, overrides, and non-self field-assignment warnings.

…ype checking

The type checker ran before codegen, so auto-generated keyword constructors
(e.g., `x:y:`), getters, and `with*:` setters for `Value subclass:` classes
were invisible to method bodies in the same class. This caused false
"does not understand" errors when a method referenced its own class's
keyword constructor (e.g., `translateX:y:` calling `Point x:y:`).

- Add `add_value_auto_methods()` to synthesize slot methods in the class
  hierarchy during semantic analysis, mirroring codegen's logic
- Extend `is_effect_free` lint to cover MapLiteral, ArrayLiteral, and
  ListLiteral (with recursive purity checks on contents)
- Add unit tests for both changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 76c343d and 8006437.

📒 Files selected for processing (1)
  • crates/beamtalk-core/src/semantic_analysis/type_checker.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/beamtalk-core/src/semantic_analysis/type_checker.rs

📝 Walkthrough

Walkthrough

Adds auto-generated Value-slot methods into class construction and reflection metadata; treats map/array/list literals as effect-free for linting; extends tests for map-literal linting and value auto-method generation; emits a warning with a functional-update hint for non-self field assignments.

Changes

Cohort / File(s) Summary
Effect-Free Statement Tests
crates/beamtalk-core/src/lint/effect_free_statement.rs
Adds two unit tests covering discarded map-literal linting: one expecting a lint for constant-value map discard, one ensuring maps with side-effectful values are not flagged.
Effect-Free Analysis
crates/beamtalk-core/src/semantic_analysis/validators.rs
Considers MapLiteral, ArrayLiteral, and ListLiteral (including optional tail) as effect-free and adds corresponding diagnostic labels.
Class Hierarchy — Auto Methods
crates/beamtalk-core/src/semantic_analysis/class_hierarchy/mod.rs
Adds add_value_auto_methods and integrates generation of instance getters, star-setters, and an optional class-side keyword constructor for Value subclasses; updates method list construction and tests.
Codegen — Reflection Metadata
crates/beamtalk-core/src/codegen/core_erlang/gen_server/methods.rs
Makes method lists mutable and appends computed auto slot methods so __beamtalk_meta/0 includes generated getters/setters/keyword constructor.
Value Type Codegen Visibility
crates/beamtalk-core/src/codegen/core_erlang/value_type_codegen.rs
Broadened visibility: AutoSlotMethods, its fields, with_star_selector, and compute_auto_slot_methods changed to pub(super) for cross-module use.
Type Checker Diagnostics
crates/beamtalk-core/src/semantic_analysis/type_checker.rs
Non-self receiver field assignments now emit a Warning diagnostic (not an Error) and attach a hint suggesting a withField:-style functional update; adds a unit test asserting a single Warning is produced.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Parser
participant ClassHierarchy
participant Validator
participant Codegen
participant RuntimeMeta
Parser->>ClassHierarchy: parse class definitions
ClassHierarchy->>ClassHierarchy: compute instance/class methods\ncompute_auto_slot_methods for Value subclasses
ClassHierarchy->>Validator: supply synthesized method info
Validator->>Validator: apply effect-free rules (include Map/Array/List literals)
ClassHierarchy->>Codegen: emit class AST with auto methods
Codegen->>RuntimeMeta: append auto getters/setters/keyword constructor\ninto reflection metadata
RuntimeMeta->>RuntimeMeta: expose __beamtalk_meta/0

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: synthesizing Value subclass auto-methods during semantic analysis before type checking, which directly addresses the root cause of the bug described in the objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch worktree-bug/value-class-new

Comment @coderabbitai help to get the list of available commands and usage tips.

jamesc and others added 2 commits February 28, 2026 22:49
…gnose non-self field assignment

- generate_meta_function now includes auto-generated slot methods
  (getters, with*: setters, keyword constructor) in __beamtalk_meta/0
  so Point methods / allMethods shows the full public interface
- Type checker emits a clear diagnostic for `other.field := value`
  instead of letting it reach codegen as an unsupported feature error;
  hints the user to use `p := p withX: newValue` instead
- Make AutoSlotMethods, compute_auto_slot_methods, and with_star_selector
  pub(super) so gen_server/methods.rs can reuse the selector logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/beamtalk-core/src/semantic_analysis/type_checker.rs`:
- Around line 280-314: The non-`self` receiver diagnostic is currently only
emitted when the receiver is an Expression::Identifier, so compound receivers
like `(obj foo).x := v` skip it; change the logic in type_checker.rs (around the
block using Expression::Identifier and check_field_assignment) to treat any
receiver that is not the identifier named "self" as an invalid mutation: call
self.check_field_assignment(field, &ty, *span, hierarchy, env) only when the
receiver is an Identifier whose name == "self", and in all other cases construct
the same Diagnostic::error / hint (with the with_sel message) and push it to
self.diagnostics so non-Identifier receivers also get the non-`self` assignment
diagnostic.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between aae763c and 63a1e4e.

📒 Files selected for processing (3)
  • crates/beamtalk-core/src/codegen/core_erlang/gen_server/methods.rs
  • crates/beamtalk-core/src/codegen/core_erlang/value_type_codegen.rs
  • crates/beamtalk-core/src/semantic_analysis/type_checker.rs

…gnostic

Compound receivers like (obj foo).x := v previously bypassed the
diagnostic and fell through to a codegen error. Now any non-self
receiver (identifier or expression) correctly emits the error with
a 'receiver' fallback name for the hint.

Addresses CodeRabbit review comment on PR #1018.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/beamtalk-core/src/semantic_analysis/type_checker.rs (1)

279-282: Consider reusing existing is_self_receiver helper.

Lines 279-282 duplicate the logic from the existing Self::is_self_receiver() method at line 566-568. Additionally, validators.rs has a more robust version that handles parenthesized expressions like (self).field := value.

Option 1: Reuse existing method
-                        let is_self_receiver = matches!(
-                            receiver.as_ref(),
-                            Expression::Identifier(recv_id) if recv_id.name == "self"
-                        );
+                        let is_self_receiver = Self::is_self_receiver(receiver);
Option 2: Enhance is_self_receiver to handle parenthesized expressions (matching validators.rs)
     /// Returns true if the expression is `self` (direct identifier reference).
     fn is_self_receiver(expr: &Expression) -> bool {
-        matches!(expr, Expression::Identifier(ident) if ident.name == "self")
+        match expr {
+            Expression::Identifier(ident) => ident.name == "self",
+            Expression::Parenthesized { expression, .. } => Self::is_self_receiver(expression),
+            _ => false,
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/beamtalk-core/src/semantic_analysis/type_checker.rs` around lines 279
- 282, Replace the duplicated local match that computes is_self_receiver in the
TypeChecker code with a call to the existing helper
Self::is_self_receiver(receiver.as_ref()); if the helper doesn't yet handle
parenthesized expressions (e.g., Expression::Paren), update
Self::is_self_receiver to mirror the robust logic in validators.rs so it treats
`(self)` the same as `self`, then use that helper from type checking (replace
the local match in the TypeChecker with the helper call to avoid duplication).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/beamtalk-core/src/semantic_analysis/type_checker.rs`:
- Around line 307-312: The diagnostic in type_checker.rs uses Diagnostic::error
when reporting "Cannot assign to `{recv_name}.{field_name}` ..." which violates
the module rule "Warnings only, never errors"; change the call to
Diagnostic::warning so the type checker emits a warning instead of an error,
preserving the same formatted message and *span variable and keeping any
attached notes or labels around that Diagnostic construction in the same place
(look for the block that builds `diag` using `recv_name`, `field_name`, and
`*span` in the `type_checker.rs` file).

---

Nitpick comments:
In `@crates/beamtalk-core/src/semantic_analysis/type_checker.rs`:
- Around line 279-282: Replace the duplicated local match that computes
is_self_receiver in the TypeChecker code with a call to the existing helper
Self::is_self_receiver(receiver.as_ref()); if the helper doesn't yet handle
parenthesized expressions (e.g., Expression::Paren), update
Self::is_self_receiver to mirror the robust logic in validators.rs so it treats
`(self)` the same as `self`, then use that helper from type checking (replace
the local match in the TypeChecker with the helper call to avoid duplication).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 63a1e4e and 76c343d.

📒 Files selected for processing (1)
  • crates/beamtalk-core/src/semantic_analysis/type_checker.rs

The type_checker module's design principle is "Warnings only, never
errors (avoid false positives)" (module docstring lines 13-14).
Changed Diagnostic::error() to Diagnostic::warning() for the
non-self field assignment diagnostic to align with this principle.

Added test asserting Severity::Warning (not Error) for `other.x := 1`.
@jamesc jamesc merged commit 213f695 into main Feb 28, 2026
5 checks passed
@jamesc jamesc deleted the worktree-bug/value-class-new branch February 28, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant